﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using PMC.Interfaces;

namespace PMC
{
    /// <summary>
    /// Logger
    /// </summary>
    public class Log
    {
        /// <summary>
        /// Singleton
        /// </summary>
        private static readonly Lazy<Log> _instance = new Lazy<Log>(() => new Log());
        private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();

        public static Log Instance
        {
            get { return _instance.Value; }
        }

        /// <summary>
        /// Log messages
        /// </summary>
        private readonly List<LogMessage> _messages = new List<LogMessage>();

        public ReadOnlyCollection<LogMessage> Messages
        {
            get { return _messages.AsReadOnly(); }
        }

        /// <summary>
        /// Adds logmessage
        /// </summary>
        /// <param name="message">Message to log</param>
        /// <param name="type">Type of logmessage</param>
        public void AddMessage(string message, LogType type = LogType.Message)
        {
            var logMessage = new LogMessage {Date = DateTime.Now, Message = message, Type = type};
            _messages.Add(logMessage);
            OnLogMessageReceived(logMessage);
        }
        
        public void AddMessage(Exception exception)
        {
            AddMessage(exception.Message + " | " + exception.InnerException + " | " + exception.StackTrace, LogType.Error);
        }

        public void AddException(Exception exception)
        {
            AddMessage(exception);
        }

        private Log()
        {
            AddMessage("---------------------- NEW SESSION ----------------------");

            var cancellationToken = _cancellationTokenSource.Token;

            Task.Factory.StartNew(() =>
                {
                    while (!cancellationToken.IsCancellationRequested)
                    {
                        SaveLog();
                        Thread.Sleep(TimeSpan.FromSeconds(30));
                    }
                }, cancellationToken);
        }

        private void SaveLog()
        {
            try
            {
                using (var text = new StreamWriter(Properties.Settings.Default.LogFile, true))
                {
                    _messages.ForEach(c => text.WriteLine(c));
                }
                _messages.Clear();
            }
            catch (Exception ex)
            {
                AddException(ex);
            }
        }


        public void OnLogMessageReceived(LogMessage message)
        {
            if (LogMessageReceived != null)
            {
                LogMessageReceived(new LogEventArgs {Message = message});
            }
        }

        ~Log()
        {
            _cancellationTokenSource.Cancel();
            AddMessage("--------------------- END OF SESSION --------------------");
            SaveLog();
        }

        public event LogEvent LogMessageReceived;

        public delegate void LogEvent(LogEventArgs args);

        public class LogEventArgs : EventArgs
        {
            public LogMessage Message { get; set; }
        }
    }

    public struct LogMessage
    {
        public DateTime Date { get; set; }
        public LogType Type { get; set; }
        public string Message { get; set; }

        public static implicit operator string(LogMessage log)
        {
            return String.Format(CultureInfo.InvariantCulture, Global.LogFormat, log.Date, log.Message, log.Type);
        }
    }

    public enum LogType
    {
        Message,
        Warning,
        Error,
    }
}
